//
// Created by wxd on 2025/9/17.
// 多光源类型的Phong氏光照渲染
//

#pragma once

#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/trigonometric.hpp>
#include "../wrapper/log/Logger.h"
#include "../wrapper/monitor/GLDebug.hpp"
#include "../global/ChineseOutput.hpp"
#include "../application/app/Application.h"
#include "../glFramework/shader/Shader.h"
#include "../glFramework/texture/Texture.h"
#include "../glFramework/utils/ResourceLoadUtils.h"
#include <memory>

#include "../global/output.hpp"
#include "../application/camera/PerspectiveCamera.h"
#include "../application/camera/control/TrackBallCameraControl.h"
#include "../application/camera/control/GameFPSCameraControl.h"

#include "../glFramework/demo/light/AmbientLight.h"
#include "../glFramework/demo/light/ParallelLight.h"
#include "../glFramework/demo/material/PhongMaterial.h"
#include "../glFramework/geo/Cube.h"
#include "../glFramework/geo/Sphere.h"
#include "../glFramework/demo/mesh/Mesh.h"
#include "../glFramework/demo/renderer/Renderer.h"
#include "../glFramework/constant/engine_uniform.h"
#include "../glFramework/manager/ShaderManager.h"
#include "../glFramework/demo/light/PointLight.h"
#include "../glFramework/demo/material/LightSourceMaterial.h"
#include "../glFramework/demo/light/SpotLight.h"
#include "../glFramework/geo/GridPlane.h"

struct MvpMatrix {
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 view = glm::mat4(1.0f);
    glm::mat4 proj = glm::mat4(1.0f);
};


inline const char* lightVertPath = "../assets/shader/phong/light_vertex.glsl";
inline const char* lightFragPath = "../assets/shader/phong/light_fragment.glsl";
inline const char* gridPlaneVertPath = "../demo/shader/phong_light_scene/color_alternate_grid_vertex.glsl";
inline const char* gridPlaneFragPath = "../demo/shader/phong_light_scene/color_alternate_grid_fragment.glsl";
inline const char* mainVertPath = "../assets/shader/phong/multi_light_phong_vertex.glsl";
inline const char* mainFragPath = "../assets/shader/phong/multi_light_phong_fragment.glsl";
inline const char* cubeDifTexPath = "../assets/texture/container2.png";
inline const char* cubeSpeTexPath = "../assets/texture/container2_specular.png";

inline MvpMatrix mvpMatrix;

inline TrackBallCameraControl mController;
inline GameFPSCameraControl mFPSController;
inline PerspectiveCamera mCamera;

inline void onResize(int w, int h) {
    glViewport(0, 0, w, h);
}

inline void onKeyBoard(int key, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE) {
        glfwSetWindowShouldClose(EngineApp.getCurrentWindow(), true);
    }

    mFPSController.onKeyBoard(key, action, mods);
}

inline void onCursor(double xpos, double ypos) {
    mFPSController.onCursor(xpos, ypos);
}

inline void onMouse(int button, int action, int mods) {
    double x, y;
    EngineApp.getCurrentCursorPos(&x, &y);
    mFPSController.onMouse(button, action, x, y);
}

inline void render() {
    ChineseOutput::init();
    EngineApp.initWindowConfig(3, 3, GLFW_OPENGL_CORE_PROFILE,
        2560, 1800,  "FPS Camera Coontrol");
    EngineApp.setResizeCallback(onResize);
    EngineApp.setKeyBoardCallback(onKeyBoard);
    EngineApp.setCursorCallback(onCursor);
    EngineApp.setMouseCallback(onMouse);
    EngineApp.setHideMouseCursor(false);
    EngineApp.init();

    std::unique_ptr<Renderer> renderer = std::make_unique<Renderer>();

    PerspectiveParams param;
    param.fov = 45.f;
    param.aspect = EngineApp.getWindowAspectRatio();
    param.zNear = 0.1f;
    param.zFar = 100.f;
    mCamera.setPerspectiveParams(param);
    mCamera.setCameraParams(glm::vec3(0.f, 0.f, 3.f), glm::vec3(0.f, 1.f, 0.f), glm::vec3(1.f, 0.f, 0.f));
    mFPSController.setMouseCenter(EngineApp.getCurrentWindowWidth(), EngineApp.getCurrentWindowHeight());
    mFPSController.setCamera(&mCamera);
    mFPSController.setSpeed(0.005f);
    mFPSController.setSensitivity(0.03f);
    mFPSController.setPitchRange(89.0f, -89.0f);

    // 几何对象
    std::unique_ptr<Cube> cubeModel = std::make_unique<Cube>(2.f, glm::vec3(0.f, 0.f, -5.f));
    std::unique_ptr<Sphere> lightModel1 = std::make_unique<Sphere>(0.07f, glm::vec3(0.f, 0.f, -3.5f), glm::vec3(1.f, 1.f, 1.f));
    std::unique_ptr<Sphere> lightModel2 = std::make_unique<Sphere>(0.07f, glm::vec3(0.f, 4.5, -5.f), glm::vec3(0.f, 1.f, 0.f));
    std::unique_ptr<Sphere> lightModel3 = std::make_unique<Sphere>(0.07f, glm::vec3(5.f, 0.f, -5.f), glm::vec3(0.f, 0.f, 1.f));
    std::unique_ptr<GridPlane> gridPlaneModel = std::make_unique<GridPlane>(
        6000.f, 6000.f, 10.f, glm::vec3(0.f, 1, 0.f), glm::vec3(0.f, -2.5f, -30.f),
        glm::vec3(0.75), glm::vec3(0.45), GridGenType::MOD
    );

    // 加载纹理
    std::unique_ptr<Texture> diffuseTexture = std::make_unique<Texture>(cubeDifTexPath, 0);
    diffuseTexture->setSamplerName(PhongMaterial::uDiffuseTexture);
    std::unique_ptr<Texture> specularTexture = std::make_unique<Texture>(cubeSpeTexPath, 1);
    specularTexture->setSamplerName(PhongMaterial::uSpecularTexture);

    // 着色器
    std::unique_ptr<Shader> lightShader = std::make_unique<Shader>(lightVertPath, lightFragPath);
    std::unique_ptr<Shader> gridPlaneShader = std::make_unique<Shader>(gridPlaneVertPath, gridPlaneFragPath);
    std::unique_ptr<Shader> mainShader = std::make_unique<Shader>(mainVertPath, mainFragPath);

    // 定义模型渲染需要的光源
    std::unique_ptr<AmbientLight> ambient = std::make_unique<AmbientLight>(
        glm::vec3(1.f), glm::vec3(1.f)
    );
    std::unique_ptr<ParallelLight> parallel = std::make_unique<ParallelLight>(
        glm::vec3(0.f, 0.f, -1.f), lightModel1->getColor(), glm::vec3(1.f)
    );
    std::unique_ptr<PointLight> point = std::make_unique<PointLight>(
    glm::vec3(3.f, 0.0f, -5.f), glm::vec3(1.f, 0.09f, 0.032f), lightModel2->getColor(), glm::vec3(1.f)
    );
    std::unique_ptr<SpotLight> spot = std::make_unique<SpotLight>(
    10.f, 15.f, glm::vec3(0.f, -1.f, 0.f)
    );
    spot->setPos(glm::vec3(0.f, 4.f, -5.f));
    spot->setColor(glm::vec3(1.0, 0., 0.));

    // 定义模型Mesh
    std::unique_ptr<PhongMaterial> cubeModelMaterial = std::make_unique<PhongMaterial>("ModelPhongMaterial");
    cubeModelMaterial->setShininess(256.f);
    cubeModelMaterial->setDiffuseTexture(std::move(diffuseTexture));
    cubeModelMaterial->setSpecularTexture(std::move(specularTexture));
    cubeModelMaterial->setEnableSpecularMask(true);
    ShaderManager::getInstance().addShader(cubeModelMaterial->getTraceId(), mainShader.get());
    std::unique_ptr<Mesh> cubeMesh = std::make_unique<Mesh>("CubeMesh");
    cubeMesh->setGeometry(std::move(cubeModel));
    cubeMesh->setMaterial(std::move(cubeModelMaterial));

    // 定义光源Mesh
    std::unique_ptr<LightSourceMaterial> parallelLightMaterial = std::make_unique<LightSourceMaterial>("parallelLightMaterial");
    std::unique_ptr<LightSourceMaterial> pointLightMaterial = std::make_unique<LightSourceMaterial>("pointLightMaterial");
    std::unique_ptr<LightSourceMaterial> spotLightMaterial = std::make_unique<LightSourceMaterial>("spotLightMaterial");
    ShaderManager::getInstance().addShader(parallelLightMaterial->getTraceId(), lightShader.get());
    ShaderManager::getInstance().addShader(pointLightMaterial->getTraceId(), lightShader.get());
    ShaderManager::getInstance().addShader(spotLightMaterial->getTraceId(), lightShader.get());
    std::unique_ptr<Mesh> parallelMesh = std::make_unique<Mesh>("ParallelLightMesh");
    std::unique_ptr<Mesh> pointMesh = std::make_unique<Mesh>("PointLightMesh");
    std::unique_ptr<Mesh> spotMesh = std::make_unique<Mesh>("SpotLightMesh");
    parallelMesh->setGeometry(std::move(lightModel1));
    parallelMesh->setMaterial(std::move(parallelLightMaterial));
    pointMesh->setGeometry(std::move(lightModel2));
    pointMesh->setMaterial(std::move(pointLightMaterial));
    spotMesh->setGeometry(std::move(lightModel3));
    spotMesh->setMaterial(std::move(spotLightMaterial));

    // 定义mesh数组
    std::vector<std::reference_wrapper<Mesh>> meshQueue;
    meshQueue.reserve(4);
    meshQueue.push_back(*cubeMesh.get());
    meshQueue.push_back(*parallelMesh.get());
    meshQueue.push_back(*pointMesh.get());
    meshQueue.push_back(*spotMesh.get());

    while (EngineApp.update()) {
        mFPSController.update();
        renderer->render_V5(mCamera, meshQueue, *ambient.get(), *parallel.get(), *point.get(), *spot.get());
    }

}
